#pragma once

#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <map>
#include <string>

#include "Vector2D.h"

#include "Logger.h"

typedef signed   char Sint8;
typedef signed   int  Sint32;
typedef unsigned int  Uint32;

typedef unsigned char   u8;
typedef unsigned short  u16;
typedef unsigned int    u32;
typedef unsigned long   u64;
typedef signed char     s8;
typedef signed short    s16;
typedef signed int      s32;
typedef signed long     s64;
typedef float           f32;
typedef double          f64;

class hgeResourceManager;

namespace tinyxml2
{
    class XMLElement;
}

namespace ZeEngine
{
    class Utilities
    {
        
    private:
        static std::map<std::string, hgeResourceManager*> m_resources;

    public:

        template< class T > 
        static void SafeDelete( T*& pVal )
        {
            delete pVal;
            pVal = 0;
        }

        template< class T > 
        static void SafeDeleteArray( T*& pVal )
        {
            delete[] pVal;
            pVal = 0;
        }

        static void Tokenize(const std::string& s, char delimiter, std::vector<std::string>& oVector)
        {
            std::string token;
            std::istringstream stream(s);

            while (getline(stream, token, delimiter))
            {
                oVector.push_back(token);
            }
        }

        template <typename T> 
        static T ConvertStringToType(const std::string& s) 
        {
            T type;
            std::stringstream stream (s);                 
            stream >> type;
            return type;
        }

        static std::string TrimString(std::string& str)
        {
            std::string::size_type pos = str.find_last_not_of(' ');
            if(pos != std::string::npos) 
            {
                str.erase(pos + 1);
                pos = str.find_first_not_of(' ');
                if(pos != std::string::npos) 
                {
                    str.erase(0, pos);
                }
            }
            else 
            {
                str.erase(str.begin(), str.end());
            }

            return str;
        }

        template <typename T>
        static bool GetAttributeType(const tinyxml2::XMLElement* pElement, const std::string& strAttributeName, T& value)
        {
            bool fSuccess = false;

            if (pElement)
            {
                const char* pValue = pElement->Attribute(strAttributeName.c_str());

                if (pValue)
                {
                    value = ConvertStringToType<T>(pValue);
                    fSuccess = true;
                }
            }

            return fSuccess;
        }

        static bool GetAttributeString(const tinyxml2::XMLElement* pElement, const std::string& strAttributeName, std::string& value);
        
        template <typename T>
        static std::vector<Vector2D<T>> ExtractCoordinatesFromString(const std::string s)
        {
            std::vector<Vector2D<T>> vector;

            istringstream stream(s);

            do
            {
                string coordinate, x, y;

                stream >> coordinate;

                if (coordinate.length() > 0)
                {
                    istringstream coordinateStream(coordinate);
                
                    getline(coordinateStream, x, ',');
                    getline(coordinateStream, y, ',');

                    vector.push_back(Vector2D<T>(ConvertStringToType<T>(x), ConvertStringToType<T>(y)));
                }

            }
            while(stream);

            return vector;
        }

        template <typename T>
        static Vector2D<T> FindMidPointBetweenVectors(const Vector2D<T>& vec1, const Vector2D<T>& vec2)
        {
            return (vec1 + vec2) / 2;
        }

        static bool PointInRectangleTest(const Vector2D<float>& point, const SDL_Rect& rect);
        static bool RectangleCollisionTest(const SDL_Rect& a, const SDL_Rect& b);
		static bool RectangleCollisionTestStrict(const SDL_Rect& a, const SDL_Rect& b);
    };
}
